package org.onionframework.persistence.query;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.onionframework.persistence.query.projection.Projections;

/**
 * Title:查询条件定义类集合<br>
 * 
 * Description:将一个完整的查询条件的每一个单个的查询条件封装到List中，支持连续操作。本类只支持查询条件之间的and关系，不支持or及()<br>
 * 
 * Company: 亚信联创集团股份有限公司<br>
 * 
 * @author author
 * @see
 * @CreateDate 2011-8-26 下午05:47:43
 * 
 */
public class OnionQuery {

	public static final String ATTR_KEY_CLASS = "class";
	
	public static final String ATTR_KEY_ALIAS = "alias";
	
	public static final String ATTR_KEY_CACHEABLE = "cacheable";
	
	public static final String ATTR_KEY_SEE_LOGICAL_REMOVED = "seeLogicalRemoved";
	
	public static final String ATTR_KEY_JOINTYPE_PREFIX = "JOINTYPE#";
	
	public static final String ATTR_KEY_JOINTYPE_PATH_DEFAULT = "ALL";
	
	/** 记录单个查询条件的集合，最用形成完整的查询条件 */
	private List<PropertyFilter> filters = new ArrayList<PropertyFilter>();
	
	private Map<String, Object> attributes = new HashMap<String,Object>(0);
	
	private LogicType logicType = LogicType.and;
	
	private Projections projections;

	/** 记录排序字段及排序方式的list */
	private List<SortProperty> sorts = new ArrayList<SortProperty>();
	
	/** 名称，用于命名查询 **/
	private String name;
	
	private int start = 0;
	
	private int limit = -1;

	/**
	 * 构造函数私有化，不允许外部类直接实例化该类
	 */
	private OnionQuery() {
		this.attribute(ATTR_KEY_JOINTYPE_PREFIX + ATTR_KEY_JOINTYPE_PATH_DEFAULT, JoinType.inner);
	}

	private OnionQuery(String name) {
		this();
		this.name = name;
	}

	public static OnionQuery instance(Class<?> entityClass){
		return new OnionQuery().attribute(ATTR_KEY_CLASS, entityClass);
	}

	public static OnionQuery orInstance(Class<?> entityClass){
		OnionQuery result =  instance(entityClass);
		result.logicType = LogicType.or;
		return result;
	}
	
	public static OnionQuery namedInstance(String name){
		return new OnionQuery(name);
	}

	/**
	 * 添加给定查询条件字段名称、字段值及匹配方式的查询条件
	 * 
	 * @param name
	 *            查询条件按字段
	 * @param value
	 *            查询条件按字段值
	 * @param matchType
	 *            查询条件匹配方式
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery add(String name, Object value, MatchType matchType) {
		return this.add(name, null, value, matchType);
	}

	/**
	 * 添加匹配方式为EQ(equals)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery add(String name, Object value) {
		return this.add(name, null, value, MatchType.EQ);
	}

	/**
	 * 添加给定查询条件字段名称、查询条件字段原名称、字段值及匹配方式的查询条件
	 * 
	 * @param name
	 *            查询条件字段名称
	 * @param originalName
	 *            查询条件字段原名称
	 * @param value
	 *            查询条件字段值
	 * @param matchType
	 *            查询条件匹配方式
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery add(String name, String originalName,
			Object value, MatchType matchType) {
		this.filters.add(new PropertyFilter(name, originalName, value,
				matchType));
		return this;
	}

	public OnionQuery addAll(OnionQuery propertyFilterList) {
		for (PropertyFilter filter : propertyFilterList.list())
			this.filters.add(filter);

		if (propertyFilterList.attributes != null) {
			if (this.attributes == null)
				this.attributes = new HashMap<String, Object>();

			this.attributes.putAll(propertyFilterList.attributes);
		}
		return this;
	}

	/**
	 * 遍历map，将map的key做查询条件字段，map的value作为查询条件字段值，将EQ(equals)作为匹配方式构造本类实例对象并返回
	 * 
	 * @param map
	 *            构造查询条件的map
	 * @return 
	 *         遍历map，将map的key做查询条件字段，map的value作为查询条件字段值，将EQ(equals)作为匹配方式构造的本类实例对象
	 */
	public OnionQuery addAll(Map<String, Object> map) {
		for (Map.Entry<String, Object> entry : map.entrySet())
			this.add(entry.getKey(), entry.getValue());
		return this;
	}
	
	public List<PropertyFilter> list() {
		return this.filters;
	}
	
	public Projections getProjections(){
		return this.projections;
	}

	public void addAttribute(String name, Object value) {
		if (this.attributes == null)
			this.attributes = new HashMap<String, Object>();

		this.attributes.put(name, value);
	}
	
	public OnionQuery attribute(String name, Object value) {
		this.addAttribute(name, value);
		return this;
	}
	
	public OnionQuery alias(String alias){
		return this.attribute(ATTR_KEY_ALIAS, alias);
	}
	
	public OnionQuery forClass(Class<?> entityClass){
		return this.attribute(ATTR_KEY_CLASS, entityClass);
	}

	public Object getAttribute(String name) {
		return this.attributes == null ? null : this.attributes.get(name);
	}
	
	public Map<String,Object> getAttributes(){
		return this.attributes;
	}

	/**
	 * 添加匹配方式为EQ(equals)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery eq(String name, Object value) {
		return this.add(name, value);
	}

	/**
	 * 添加匹配方式为NE(不匹配)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery ne(String name, Object value) {
		return this.add(name, value, MatchType.NE);
	}

	/**
	 * 添加匹配方式为IEQ(忽略大小写匹配)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery ieq(String name, Object value) {
		return this.add(name, value, MatchType.IEQ);
	}

	/**
	 * 添加匹配方式为LIKE(like)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery like(String name, Object value) {
		return this.add(name, value, MatchType.LIKE);
	}

	/**
	 * 添加匹配方式为 LLIKE(left like)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery llike(String name, Object value) {
		return this.add(name, value, MatchType.LLIKE);
	}

	/**
	 * 添加匹配方式为RLIKE(right like)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery rlike(String name, Object value) {
		return this.add(name, value, MatchType.RLIKE);
	}

	/**
	 * 添加匹配方式为ILIKE(ignore case like)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery ilike(String name, Object value) {
		return this.add(name, value, MatchType.ILIKE);
	}

	/**
	 * 添加匹配方式为LT(less than)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery lt(String name, Object value) {
		return this.add(name, value, MatchType.LT);
	}

	/**
	 * 添加匹配方式为GT(greater then)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery gt(String name, Object value) {
		return this.add(name, value, MatchType.GT);
	}

	/**
	 * 添加匹配方式为LE(小于等于)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery le(String name, Object value) {
		return this.add(name, value, MatchType.LE);
	}

	/**
	 * 添加匹配方式为GE(大于等于)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery ge(String name, Object value) {
		return this.add(name, value, MatchType.GE);
	}

	/**
	 * 添加匹配方式为GE(大于等于)的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值,为Object[2]数组
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery between(String name, Object value) {
		return this.add(name, value, MatchType.BETWEEN);
	}

	/**
	 * 添加匹配方式为IN的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery in(String name, Object[] value) {
		return this.add(name, value, MatchType.IN);
	}

	/**
	 * 添加匹配方式为IN的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery in(String name, Collection<?> value) {
		return this.add(name, value, MatchType.IN);
	}

	/**
	 * 添加匹配方式为NOT_IN的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery notIn(String name, Object[] value) {
		return this.add(name, value, MatchType.NOT_IN);
	}

	/**
	 * 添加匹配方式为NOT_IN的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery notIn(String name, Collection<?> value) {
		return this.add(name, value, MatchType.NOT_IN);
	}

	/**
	 * 添加匹配方式为HAS的查询条件
	 * 
	 * @param name
	 *            查询条件字段
	 * @param value
	 *            查询条件值
	 * @return 调用该方法的本类实例对象自身，为的是支持连续操作
	 */
	public OnionQuery has(String name, OnionQuery value) {
		return this.add(name, value, MatchType.HAS);
	}
	
	public OnionQuery sizeEq(String name, Integer size){
		return this.add(name, size, MatchType.SIZE_EQ);
	}
	
	public OnionQuery sizeGe(String name, Integer size){
		return this.add(name, size, MatchType.SIZE_GE);
	}
	
	public OnionQuery sizeGt(String name, Integer size){
		return this.add(name, size, MatchType.SIZE_GT);
	}
	
	public OnionQuery sizeLe(String name, Integer size){
		return this.add(name, size, MatchType.SIZE_LE);
	}
	
	public OnionQuery sizeLt(String name, Integer size){
		return this.add(name, size, MatchType.SIZE_LT);
	}

	public OnionQuery eqProperty(String name,String otherPropertyName){
		return this.add(name, otherPropertyName, MatchType.EQ_PROPERTY);
	}
	
	public OnionQuery exists(String name,OnionQuery propertyFilterList){
		return this.add(name, propertyFilterList, MatchType.EXISTS);
	}
	
	public OnionQuery seeLogicalRemoved(){
		this.addAttribute(ATTR_KEY_SEE_LOGICAL_REMOVED, true);
		return this;
	}
	
	public LogicType getLogicType(){
		return logicType;
	}

	
	public OnionQuery dateStringEq(String name,String value,String pattern){
		this.filters.add(new PropertyFilter(name,new String[]{pattern,value},MatchType.DATE_STRING_EQ));
		return this;
	}
	
	public OnionQuery dateStringLike(String name,String value,String pattern){
		this.filters.add(new PropertyFilter(name,new String[]{pattern,value},MatchType.DATE_STRING_LIKE));
		return this;
	}
	
	public OnionQuery datePartEq(String name,Integer value,DatePart datePart){
		this.filters.add(new PropertyFilter(name,new Object[]{datePart,value},MatchType.DATE_PART_EQ));
		return this;
	}
	
	public OnionQuery datePartLe(String name,Integer value,DatePart datePart){
		this.filters.add(new PropertyFilter(name,new Object[]{datePart,value},MatchType.DATE_PART_LE));
		return this;
	}
	
	public OnionQuery datePartLt(String name,Integer value,DatePart datePart){
		this.filters.add(new PropertyFilter(name,new Object[]{datePart,value},MatchType.DATE_PART_LT));
		return this;
	}
	
	public OnionQuery datePartGe(String name,Integer value,DatePart datePart){
		this.filters.add(new PropertyFilter(name,new Object[]{datePart,value},MatchType.DATE_PART_GE));
		return this;
	}
	
	public OnionQuery datePartGt(String name,Integer value,DatePart datePart){
		this.filters.add(new PropertyFilter(name,new Object[]{datePart,value},MatchType.DATE_PART_GT));
		return this;
	}
	
	public OnionQuery not(String name,Object value,MatchType matchType){
		this.filters.add(new PropertyFilter("not_" + name,
				new PropertyFilter(name,value,matchType),
				MatchType.NOT));
		return this;
	}
	
	/**
	 * 查询条件片段，相当于括号括起来的一个片段
	 */
	public OnionQuery fragment(String name,OnionQuery propertyFilterList){
		return this.add(name, propertyFilterList, MatchType.FRAGMENT);
	}
	
	public OnionQuery remove(String name){
		Iterator<PropertyFilter> iterator = this.filters.iterator();
		PropertyFilter filter;
		while(iterator.hasNext()){
			filter = iterator.next();
			if(filter.getPropertyName().equals(name))
				iterator.remove();
		}
		return this;
	}
	
	public OnionQuery projections(Projections projections){
		this.projections = projections;
		return this;
	}
	
	public OnionQuery leftJoin(String path){
		return this.attribute(ATTR_KEY_JOINTYPE_PREFIX + path, JoinType.left);
	}
	
	public OnionQuery innerJoin(String path){
		return this.attribute(ATTR_KEY_JOINTYPE_PREFIX + path, JoinType.inner);
	}
	
	public OnionQuery cacheable(){
		return this.attribute(ATTR_KEY_CACHEABLE, true);
	}
	
	
	/**
	 * Title:排序方式<br>
	 * 
	 * Description:升序排列和降序排列两种方式<br>
	 * 
	 * Company: 亚信联创集团股份有限公司<br>
	 * 
	 * @author allan
	 * @see
	 * @CreateDate 2011-8-29 上午09:28:30
	 * 
	 */
	public static enum SortType {
		ASC, DESC
	}

	/**
	 * 将参数封装成SortProperty对象后添加到list中
	 * 
	 * @param propertyName
	 *            排序字段
	 * @param sortType
	 *            排序方式
	 * @return 调用此方法的SortProperties实例自身
	 */
	public OnionQuery addSort(String propertyName, SortType sortType) {
		this.sorts.add(new SortProperty(propertyName, sortType));
		return this;
	}

	/**
	 * 将排序字段按asc升序排序方式添加到当前对象中
	 * 
	 * @param propertyName
	 *            排序字段
	 * @return 调用本方法的SortProperties对象自身
	 */
	public OnionQuery asc(String propertyName) {
		return this.addSort(propertyName, SortType.ASC);
	}

	/**
	 * 将排序字段按desc降序排序方式添加到当前对象中
	 * 
	 * @param propertyName
	 *            排序字段
	 * @return 调用本方法的SortProperties对象自身
	 */
	public OnionQuery desc(String propertyName) {
		return this.addSort(propertyName, SortType.DESC);
	}

	/**
	 * 循环遍历sortProperties对象，将sortProperties对象所有排序方式添加到当前对象中<br>
	 * 此方法存在如下可能：当前对象已经存在按name asc排序，而传进来的sortProperties含有按name desc排序<br>
	 * 因此如果调用此方法，应慎重
	 * 
	 * @param sortProperties
	 * @return
	 */
	public OnionQuery addAll(List<SortProperty> sorts) {
		for (SortProperty sp : sorts)
			this.sorts.add(sp);

		return this;
	}

	public List<SortProperty> sorts() {
		return this.sorts;
	}
	
	public String getName() {
		return name;
	}

	public boolean isNamedQuery(){
		return StringUtils.isNotBlank(this.name);
	}
	
	public Class<?> getForClass(){
		return (Class<?>) this.getAttribute(ATTR_KEY_CLASS);
	}

	public int getStart() {
		return start;
	}
	
	/**
	 * 设置分页查询的起始记录序号
	 */
	public OnionQuery start(int start){
		this.start = start;
		return this;
	}

	/**
	 * 设置分页查询的记录数
	 */
	public int getLimit() {
		return limit;
	}

	public OnionQuery limit(int limit){
		this.limit = limit;
		return this;
	}

	/**
	 * Title:记录数据库查询SQL排序信息的类<br>
	 * 
	 * Description: 记录数据库查询语句排序字段和排序方式<br>
	 * 
	 * Company: 亚信联创集团股份有限公司<br>
	 * 
	 * @author allan
	 * @see
	 * @CreateDate 2011-8-29 上午09:24:21
	 * 
	 */
	public static class SortProperty {

		/** 排序字段 */
		private String propertyName;

		/** 排序方式，有asc和desc两种排序方式 */
		private SortType sortType;

		/**
		 * 构造器
		 * 
		 * @param propertyName
		 *            排序字段
		 * @param sortType
		 *            排序方式
		 */
		public SortProperty(String propertyName, SortType sortType) {
			this.propertyName = propertyName;
			this.sortType = sortType;
		}

		public String getPropertyName() {
			return propertyName;
		}

		public SortType getSortType() {
			return sortType;
		}

		public String toString() {
			return "{propertyName:" + propertyName + ", sortType:"
					+ sortType.name() + "}";
		}
	}

}
